/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information

Abstract:
Part of Core Audio AUBase Classes
*/

#ifndef __AUBase_h__
#define __AUBase_h__

#include <TargetConditionals.h>

#if TARGET_OS_MAC
    #include <pthread.h>
#elif TARGET_OS_WIN32
    #include <windows.h>
#else
    #error Unsupported Operating System
#endif

#include <vector>

#include "AUScopeElement.h"
#include "AUInputElement.h"
#include "AUOutputElement.h"
#include "AUBuffer.h"
#include "CAMath.h"
#include "CAThreadSafeList.h"
#include "CAVectorUnit.h"
#include "CAMutex.h"
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
    #include <AudioUnit/AudioUnit.h>
    #if !CA_BASIC_AU_FEATURES
        #include <AudioUnit/MusicDevice.h>
    #endif
#else
    #include "AudioUnit.h"
    #if !CA_BASIC_AU_FEATURES
        #include "MusicDevice.h"
    #endif
#endif

#ifndef AUTRACE
    #define AUTRACE(code, obj, a, b, c, d)
#endif

#include "AUPlugInDispatch.h"

#ifndef CA_CANONICAL_DEPRECATED
    #define CA_CANONICAL_DEPRECATED
#endif

// ________________________________________________________________________
// These are to be moved to the public AudioUnit headers

#define kAUDefaultSampleRate        44100.0
#if !TARGET_OS_WIN32
#define kAUDefaultMaxFramesPerSlice 1156
//this allows enough default frames for a 512 dest 44K and SRC from 96K
// add a padding of 4 frames for any altivec rounding
#else
#define kAUDefaultMaxFramesPerSlice 2048
#endif

// ________________________________________________________________________

/*! @class AUBase */
class AUBase : public ComponentBase {
public:

    /*! @ctor AUBase */
                                AUBase(                 AudioComponentInstance          inInstance,
                                                        UInt32                          numInputElements,
                                                        UInt32                          numOutputElements,
                                                        UInt32                          numGroupElements = 0);
    /*! @dtor AUBase */
    virtual                     ~AUBase();

    /*! @method PostConstructor */
    virtual void                PostConstructor() { CreateElements(); }

    /*! @method PreDestructor */
    virtual void                PreDestructor();

    /*! @method CreateElements */
    void                        CreateElements();
                                    // Called immediately after construction, when virtual methods work.
                                    // Or, a subclass may call this in order to have access to elements
                                    // in its constructor.

    /*! @method CreateExtendedElements */
    virtual void CreateExtendedElements() {}

#pragma mark -
#pragma mark AU dispatch
    // ________________________________________________________________________
    // Virtual methods (mostly) directly corresponding to the entry points.  Many of these
    // have useful implementations here and will not need overriding.

    /*! @method DoInitialize */
            OSStatus            DoInitialize();
                // this implements the entry point and makes sure that initialization
                // is only attempted exactly once...

    /*! @method Initialize */
    virtual OSStatus            Initialize();
                // ... so that overrides to this method can assume that they will only
                // be called exactly once.

    /*! @method IsInitialized */
            bool                IsInitialized() const { return mInitialized; }
    /*! @method HasBegunInitializing */
            bool                HasBegunInitializing() const { return mHasBegunInitializing; }

    /*! @method DoCleanup */
            void                DoCleanup();
                // same pattern as with Initialize

    /*! @method Cleanup */
    virtual void                Cleanup();

    /*! @method Reset */
    virtual OSStatus            Reset(                  AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement);

    // Note about GetPropertyInfo, GetProperty, SetProperty:
    // Certain properties are trapped out in these dispatch functions and handled with different virtual
    // methods.  (To discourage hacks and keep vtable size down, these are non-virtual)

    /*! @method DispatchGetPropertyInfo */
            OSStatus            DispatchGetPropertyInfo(AudioUnitPropertyID             inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement,
                                                        UInt32 &                        outDataSize,
                                                        Boolean &                       outWritable);

    /*! @method DispatchGetProperty */
            OSStatus            DispatchGetProperty(    AudioUnitPropertyID             inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement,
                                                        void *                          outData);

    /*! @method DispatchSetProperty */
            OSStatus            DispatchSetProperty(    AudioUnitPropertyID             inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement,
                                                        const void *                    inData,
                                                        UInt32                          inDataSize);

            OSStatus            DispatchRemovePropertyValue(    AudioUnitPropertyID     inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement);

    /*! @method GetPropertyInfo */
    virtual OSStatus            GetPropertyInfo(        AudioUnitPropertyID             inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement,
                                                        UInt32 &                        outDataSize,
                                                        Boolean &                       outWritable);

    /*! @method GetProperty */
    virtual OSStatus            GetProperty(            AudioUnitPropertyID             inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement,
                                                        void *                          outData);

    /*! @method SetProperty */
    virtual OSStatus            SetProperty(            AudioUnitPropertyID             inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement,
                                                        const void *                    inData,
                                                        UInt32                          inDataSize);

    /*! @method ClearPropertyUsage */
    virtual OSStatus            RemovePropertyValue (   AudioUnitPropertyID             inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement);

    /*! @method AddPropertyListener */
    virtual OSStatus            AddPropertyListener(    AudioUnitPropertyID             inID,
                                                        AudioUnitPropertyListenerProc   inProc,
                                                        void *                          inProcRefCon);

    /*! @method RemovePropertyListener */
    virtual OSStatus            RemovePropertyListener( AudioUnitPropertyID             inID,
                                                        AudioUnitPropertyListenerProc   inProc,
                                                        void *                          inProcRefCon,
                                                        bool                            refConSpecified);

    /*! @method SetRenderNotification */
    virtual OSStatus            SetRenderNotification(  AURenderCallback                inProc,
                                                        void *                          inRefCon);

    /*! @method RemoveRenderNotification */
    virtual OSStatus            RemoveRenderNotification(
                                                        AURenderCallback                inProc,
                                                        void *                          inRefCon);

    /*! @method GetParameter */
    virtual OSStatus    GetParameter(           AudioUnitParameterID            inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement,
                                                        AudioUnitParameterValue &       outValue);

    /*! @method SetParameter */
    virtual OSStatus    SetParameter(           AudioUnitParameterID            inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement,
                                                        AudioUnitParameterValue         inValue,
                                                        UInt32                          inBufferOffsetInFrames);

    /*! @method CanScheduleParams */
    virtual bool CanScheduleParameters() const = 0;

    /*! @method ScheduleParameter */
    virtual OSStatus    ScheduleParameter (     const AudioUnitParameterEvent   *inParameterEvent,
                                                        UInt32                          inNumEvents);


    /*! @method DoRender */
    OSStatus    DoRender(                               AudioUnitRenderActionFlags &    ioActionFlags,
                                                        const AudioTimeStamp &          inTimeStamp,
                                                        UInt32                          inBusNumber,
                                                        UInt32                          inNumberFrames,
                                                        AudioBufferList &               ioData);


    /*! @method Process */
    OSStatus    DoProcess (                         AudioUnitRenderActionFlags  &       ioActionFlags,
                                                    const AudioTimeStamp &              inTimeStamp,
                                                    UInt32                              inFramesToProcess,
                                                    AudioBufferList &                   ioData);

    /*! @method ProcessMultiple */
    OSStatus    DoProcessMultiple (                 AudioUnitRenderActionFlags  &       ioActionFlags,
                                                    const AudioTimeStamp &              inTimeStamp,
                                                    UInt32                              inFramesToProcess,
                                                    UInt32                              inNumberInputBufferLists,
                                                    const AudioBufferList **            inInputBufferLists,
                                                    UInt32                              inNumberOutputBufferLists,
                                                    AudioBufferList **                  ioOutputBufferLists);

    /*! @method ProcessBufferLists */
    virtual OSStatus    ProcessBufferLists(         AudioUnitRenderActionFlags &        ioActionFlags,
                                                    const AudioBufferList &             inBuffer,
                                                    AudioBufferList &                   outBuffer,
                                                    UInt32                              inFramesToProcess )
                        {
                            return kAudio_UnimplementedError;
                        }

    /*! @method ProcessMultipleBufferLists */
    virtual OSStatus    ProcessMultipleBufferLists( AudioUnitRenderActionFlags &        ioActionFlags,
                                                    UInt32                              inFramesToProcess,
                                                    UInt32                              inNumberInputBufferLists,
                                                    const AudioBufferList **            inInputBufferLists,
                                                    UInt32                              inNumberOutputBufferLists,
                                                    AudioBufferList **                  ioOutputBufferLists)
                        {
                            return kAudio_UnimplementedError;
                        }

    /*! @method ComplexRender */
    virtual OSStatus    ComplexRender(              AudioUnitRenderActionFlags &        ioActionFlags,
                                                    const AudioTimeStamp &              inTimeStamp,
                                                    UInt32                              inOutputBusNumber,
                                                    UInt32                              inNumberOfPackets,
                                                    UInt32 *                            outNumberOfPackets,
                                                    AudioStreamPacketDescription *      outPacketDescriptions,
                                                    AudioBufferList &                   ioData,
                                                    void *                              outMetadata,
                                                    UInt32 *                            outMetadataByteSize)
                        {
                            return kAudio_UnimplementedError;
                        }

    // Override this method if your AU processes multiple output busses completely independently --
    // you'll want to just call Render without the NeedsToRender check.
    // Otherwise, override Render().
    //
    // N.B. Implementations of this method can assume that the output's buffer list has already been
    // prepared and access it with GetOutput(inBusNumber)->GetBufferList() instead of
    // GetOutput(inBusNumber)->PrepareBuffer(nFrames) -- if PrepareBuffer is called, a
    // copy may occur after rendering.
    /*! @method RenderBus */
    virtual OSStatus            RenderBus(              AudioUnitRenderActionFlags &    ioActionFlags,
                                                        const AudioTimeStamp &          inTimeStamp,
                                                        UInt32                          inBusNumber,
                                                        UInt32                          inNumberFrames)
                                {
                                    if (NeedsToRender(inTimeStamp))
                                        return Render(ioActionFlags, inTimeStamp, inNumberFrames);
                                    return noErr;   // was presumably already rendered via another bus
                                }

    // N.B. For a unit with only one output bus, it can assume in its implementation of this
    // method that the output's buffer list has already been prepared and access it with
    // GetOutput(0)->GetBufferList() instead of GetOutput(0)->PrepareBuffer(nFrames)
    //  -- if PrepareBuffer is called, a copy may occur after rendering.
    /*! @method Render */
    virtual OSStatus            Render(                 AudioUnitRenderActionFlags &    ioActionFlags,
                                                        const AudioTimeStamp &          inTimeStamp,
                                                        UInt32                          inNumberFrames)
                                {
                                    return noErr;
                                }


#pragma mark -
#pragma mark Property Dispatch

    static const Float64 kNoLastRenderedSampleTime;

    // ________________________________________________________________________
    // These are generated from DispatchGetProperty/DispatchGetPropertyInfo/DispatchSetProperty

    /*! @method BusCountWritable */
    virtual bool                BusCountWritable(       AudioUnitScope                  inScope)
                                {
                                    return false;
                                }
    virtual OSStatus            SetBusCount(        AudioUnitScope                  inScope,
                                                    UInt32                          inCount);

    /*! @method SetConnection */
    virtual OSStatus            SetConnection(          const AudioUnitConnection &     inConnection);

    /*! @method SetInputCallback */
    virtual OSStatus            SetInputCallback(       UInt32                          inPropertyID,
                                                        AudioUnitElement                inElement,
                                                        AURenderCallback                inProc,
                                                        void *                          inRefCon);

    /*! @method GetParameterList */
    virtual OSStatus            GetParameterList(       AudioUnitScope                  inScope,
                                                        AudioUnitParameterID *          outParameterList,
                                                        UInt32 &                        outNumParameters);
                                                            // outParameterList may be a null pointer

    /*! @method GetParameterInfo */
    virtual OSStatus            GetParameterInfo(       AudioUnitScope                  inScope,
                                                        AudioUnitParameterID            inParameterID,
                                                        AudioUnitParameterInfo &        outParameterInfo);

    virtual OSStatus            GetParameterHistoryInfo(AudioUnitScope                  inScope,
                                                        AudioUnitParameterID            inParameterID,
                                                        Float32 &                       outUpdatesPerSecond,
                                                        Float32 &                       outHistoryDurationInSeconds);

    /*! @method SaveState */
    virtual OSStatus            SaveState(              CFPropertyListRef *             outData);

    /*! @method SaveExtendedScopes */
    virtual void                SaveExtendedScopes(     CFMutableDataRef                outData) {};

    /*! @method RestoreState */
    virtual OSStatus            RestoreState(           CFPropertyListRef               inData);

    /*! @method GetParameterValueStrings */
    virtual OSStatus            GetParameterValueStrings(AudioUnitScope                 inScope,
                                                        AudioUnitParameterID            inParameterID,
                                                        CFArrayRef *                    outStrings);

    /*! @method CopyClumpName */
    virtual OSStatus            CopyClumpName(          AudioUnitScope                  inScope,
                                                        UInt32                          inClumpID,
                                                        UInt32                          inDesiredNameLength,
                                                        CFStringRef *                   outClumpName);

    /*! @method GetPresets */
    virtual OSStatus            GetPresets (            CFArrayRef *                    outData) const;

        // set the default preset for the unit -> the number of the preset MUST be >= 0
        // and the name should be valid, or the preset WON'T take
    /*! @method SetAFactoryPresetAsCurrent */
    bool                        SetAFactoryPresetAsCurrent (const AUPreset & inPreset);

        // Called when someone sets a new, valid preset
        // If this is a valid preset, then the subclass sets its state to that preset
        // and returns noErr.
        // If not a valid preset, return an error, and the pre-existing preset is restored
    /*! @method NewFactoryPresetSet */
    virtual OSStatus            NewFactoryPresetSet (const AUPreset & inNewFactoryPreset);

    /*! @method NewCustomPresetSet */
    virtual OSStatus            NewCustomPresetSet (const AUPreset & inNewCustomPreset);

#if !CA_USE_AUDIO_PLUGIN_ONLY
    /*! @method GetNumCustomUIComponents */
    virtual int                 GetNumCustomUIComponents ();

    /*! @method GetUIComponentDescs */
    virtual void                GetUIComponentDescs (ComponentDescription* inDescArray);
#endif

    /*! @method CopyIconLocation */
    virtual CFURLRef            CopyIconLocation ();

    // default is no latency, and unimplemented tail time
    /*! @method GetLatency */
    virtual Float64             GetLatency() {return 0.0;}
    /*! @method GetTailTime */
    virtual Float64             GetTailTime() {return 0;}
    /*! @method SupportsRampAndTail */
    virtual bool                SupportsTail () { return false; }

    /*! @method IsStreamFormatWritable */
            bool                IsStreamFormatWritable( AudioUnitScope                  scope,
                                                        AudioUnitElement                element);

    /*! @method StreamFormatWritable */
    virtual bool                StreamFormatWritable(   AudioUnitScope                  scope,
                                                        AudioUnitElement                element) = 0;
                                                            // scope will always be input or output

            // pass in a pointer to get the struct, and num channel infos
            // you can pass in NULL to just get the number
            // a return value of 0 (the default in AUBase) means the property is not supported...
    /*! @method SupportedNumChannels */
    virtual UInt32              SupportedNumChannels (  const AUChannelInfo**           outInfo);

    /*! @method ValidFormat */
    virtual bool                ValidFormat(            AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement,
                                                        const CAStreamBasicDescription & inNewFormat);
                                                            // Will only be called after StreamFormatWritable
                                                            // has succeeded.
                                                            // Default implementation requires canonical format:
                                                            // native-endian 32-bit float, any sample rate,
                                                            // any number of channels; override when other
                                                            // formats are supported.  A subclass's override can
                                                            // choose to always return true and trap invalid
                                                            // formats in ChangeStreamFormat.


    /*! @method FormatIsCanonical */
            CA_CANONICAL_DEPRECATED
            bool                FormatIsCanonical(      const CAStreamBasicDescription &format);

    /*! @method MakeCanonicalFormat */
            CA_CANONICAL_DEPRECATED
            void                MakeCanonicalFormat(    CAStreamBasicDescription &  outDesc,
                                                        int                         numChannels = 2);

    /*! @method GetStreamFormat */
    virtual const CAStreamBasicDescription &
                                GetStreamFormat(        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement);

    /*! @method ChangeStreamFormat */
    virtual OSStatus            ChangeStreamFormat(     AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement,
                                                        const CAStreamBasicDescription & inPrevFormat,
                                                        const CAStreamBasicDescription & inNewFormat);
                                                            // Will only be called after StreamFormatWritable
                                                            // and ValidFormat have succeeded.

    // ________________________________________________________________________

#if !CA_USE_AUDIO_PLUGIN_ONLY
    /*! @method ComponentEntryDispatch */
    static OSStatus         ComponentEntryDispatch( ComponentParameters *           params,
                                                        AUBase *                        This);
#endif

    // ________________________________________________________________________
    // Methods useful for subclasses

    /*! @method GetScope */
    AUScope &                   GetScope(               AudioUnitScope                  inScope)
    {
        if (inScope >= kNumScopes) {
            AUScope * scope = GetScopeExtended(inScope);
            if (!scope) COMPONENT_THROW(kAudioUnitErr_InvalidScope);
            return *scope;
        }
        return mScopes[inScope];
    }

    /*! @method GetScopeExtended */
    virtual AUScope *           GetScopeExtended (AudioUnitScope inScope) { return NULL; }

    /*! @method GlobalScope */
    AUScope &                   GlobalScope() { return mScopes[kAudioUnitScope_Global]; }
    /*! @method Inputs */
    AUScope &                   Inputs()    { return mScopes[kAudioUnitScope_Input]; }
    /*! @method Outputs */
    AUScope &                   Outputs()   { return mScopes[kAudioUnitScope_Output]; }
#if !CA_BASIC_AU_FEATURES
    /*! @method Groups */
    AUScope &                   Groups()    { return mScopes[kAudioUnitScope_Group]; }
#endif
    /*! @method Globals */
    AUElement *                 Globals()   { return mScopes[kAudioUnitScope_Global].GetElement(0); }

    /*! @method SetNumberOfElements */
    void                        SetNumberOfElements(    AudioUnitScope                  inScope,
                                                        UInt32                          numElements);

    /*! @method GetElement */
    AUElement *                 GetElement(             AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement)
    {
        return GetScope(inScope).GetElement(inElement);
    }

    /*! @method GetIOElement */
    AUIOElement *               GetIOElement(           AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement)
    {
        return GetScope(inScope).GetIOElement(inElement);
    }

    /*! @method SafeGetElement */
    AUElement *                 SafeGetElement(         AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement)
    {
        return GetScope(inScope).SafeGetElement(inElement);
    }

    /*! @method GetInput */
    AUInputElement *            GetInput(               AudioUnitElement                inElement)
    {
        return static_cast<AUInputElement *>(Inputs().SafeGetElement(inElement));
    }

    /*! @method GetOutput */
    AUOutputElement *           GetOutput(              AudioUnitElement                inElement)
    {
        return static_cast<AUOutputElement *>(Outputs().SafeGetElement(inElement));
    }

#if !CA_BASIC_AU_FEATURES
    /*! @method GetGroup */
    AUElement *                 GetGroup(               AudioUnitElement                inElement)
    {
        return Groups().SafeGetElement(inElement);
    }
#endif

    /*! @method PullInput */
    OSStatus                    PullInput(              UInt32                      inBusNumber,
                                                        AudioUnitRenderActionFlags &ioActionFlags,
                                                        const AudioTimeStamp &      inTimeStamp,
                                                        UInt32                      inNumberFrames)
    {
        AUInputElement *input = GetInput(inBusNumber);  // throws if error
        return input->PullInput(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames);
    }

    /*! @method GetMaxFramesPerSlice */
    UInt32                      GetMaxFramesPerSlice() const { return mMaxFramesPerSlice; }
    /*! @method UsesFixedBlockSize */
    bool                        UsesFixedBlockSize() const { return mUsesFixedBlockSize; }
    /*! @method SetUsesFixedBlockSize */
    void                        SetUsesFixedBlockSize(bool inUsesFixedBlockSize) { mUsesFixedBlockSize = inUsesFixedBlockSize; }

    /*! @method GetVectorUnitType */
    static SInt32               GetVectorUnitType() { return sVectorUnitType; }
    /*! @method HasVectorUnit */
    static bool                 HasVectorUnit() { return sVectorUnitType > 0; }
    /*! @method HasAltivec */
    static bool                 HasAltivec() { return sVectorUnitType == kVecAltivec; }
    /*! @method HasSSE2 */
    static bool                 HasSSE2() { return sVectorUnitType >= kVecSSE2; }
    /*! @method HasSSE3 */
    static bool                 HasSSE3() { return sVectorUnitType >= kVecSSE3; }

    /*! @method AudioUnitAPIVersion */
    UInt8                       AudioUnitAPIVersion() const { return mAudioUnitAPIVersion; }

    /*! @method IsRenderThread */
    bool                        InRenderThread () const
                                {
#if TARGET_OS_MAC
                                    return (mRenderThreadID ? pthread_equal (mRenderThreadID, pthread_self()) : false);
#elif TARGET_OS_WIN32
                                    return (mRenderThreadID ? mRenderThreadID == GetCurrentThreadId() : false);
#endif
                                }

    /*! @method HasInput */
    bool                        HasInput(               AudioUnitElement                inElement) {
                                    AUInputElement *in = static_cast<AUInputElement *>(Inputs().GetElement(inElement));
                                    return in != NULL && in->IsActive();
                                }
                                    // says whether an input is connected or has a callback

    /*! @method PropertyChanged */
    virtual void                PropertyChanged(        AudioUnitPropertyID             inID,
                                                        AudioUnitScope                  inScope,
                                                        AudioUnitElement                inElement);

#if !CA_NO_AU_UI_FEATURES
    // These calls can be used to call a Host's Callbacks. The method returns -1 if the host
    // hasn't supplied the callback. Any other result is returned by the host.
    // As in the API contract, for a parameter's value, you specify a pointer
    // to that data type. Specify NULL for a parameter that you are not interested
    // as this can save work in the host.

    /*! @method CallHostBeatAndTempo */
    OSStatus    CallHostBeatAndTempo (Float64               *outCurrentBeat,
                                        Float64             *outCurrentTempo)
    {
        return (mHostCallbackInfo.beatAndTempoProc
                        ? (*mHostCallbackInfo.beatAndTempoProc) (mHostCallbackInfo.hostUserData,
                                                                    outCurrentBeat,
                                                                    outCurrentTempo)
                        : -1);
    }

    /*! @method CallHostMusicalTimeLocation */
    OSStatus    CallHostMusicalTimeLocation (UInt32         *outDeltaSampleOffsetToNextBeat,
                                        Float32             *outTimeSig_Numerator,
                                        UInt32              *outTimeSig_Denominator,
                                        Float64             *outCurrentMeasureDownBeat)
    {
        return (mHostCallbackInfo.musicalTimeLocationProc
                        ? (*mHostCallbackInfo.musicalTimeLocationProc) (mHostCallbackInfo.hostUserData,
                                                                            outDeltaSampleOffsetToNextBeat,
                                                                            outTimeSig_Numerator,
                                                                            outTimeSig_Denominator,
                                                                            outCurrentMeasureDownBeat)
                        : -1);
    }

    /*! @method CallHostTransportState */
    OSStatus    CallHostTransportState (Boolean             *outIsPlaying,
                                        Boolean             *outTransportStateChanged,
                                        Float64             *outCurrentSampleInTimeLine,
                                        Boolean             *outIsCycling,
                                        Float64             *outCycleStartBeat,
                                        Float64             *outCycleEndBeat)
    {
        return (mHostCallbackInfo.transportStateProc
                        ? (*mHostCallbackInfo.transportStateProc) (mHostCallbackInfo.hostUserData,
                                                                        outIsPlaying,
                                                                        outTransportStateChanged,
                                                                        outCurrentSampleInTimeLine,
                                                                        outIsCycling,
                                                                        outCycleStartBeat,
                                                                        outCycleEndBeat)
                        : -1);
    }
#endif

    char*                       GetLoggingString () const;

    CAMutex*                    GetMutex() { return mAUMutex; }

    // ________________________________________________________________________
    /*! @method CreateElement */
    virtual AUElement *         CreateElement(          AudioUnitScope                  scope,
                                                        AudioUnitElement                element);

#pragma mark -
#pragma mark AU Output Base Dispatch
    // ________________________________________________________________________
    // ________________________________________________________________________
    // ________________________________________________________________________
    // output unit methods
    /*! @method Start */
    virtual OSStatus    Start() { return kAudio_UnimplementedError; }
    /*! @method Stop */
    virtual OSStatus    Stop() { return kAudio_UnimplementedError; }

#if !CA_BASIC_AU_FEATURES
#pragma mark -
#pragma mark AU Music Base Dispatch

#if !TARGET_OS_IPHONE
// these methods are deprecated, so we don't include them except for compatability
    /*! @method PrepareInstrument */
    virtual OSStatus            PrepareInstrument(MusicDeviceInstrumentID inInstrument) { return kAudio_UnimplementedError; }

    /*! @method PrepareInstrument */
    virtual OSStatus            ReleaseInstrument(MusicDeviceInstrumentID inInstrument) { return kAudio_UnimplementedError; }
#endif

    // ________________________________________________________________________
    // ________________________________________________________________________
    // ________________________________________________________________________
    // music device/music effect methods -- incomplete
    /*! @method MIDIEvent */
    virtual OSStatus    MIDIEvent(      UInt32                      inStatus,
                                        UInt32                      inData1,
                                        UInt32                      inData2,
                                        UInt32                      inOffsetSampleFrame) { return kAudio_UnimplementedError; }

    /*! @method SysEx */
    virtual OSStatus    SysEx(          const UInt8 *               inData,
                                        UInt32                      inLength) { return kAudio_UnimplementedError;}

    /*! @method StartNote */
    virtual OSStatus    StartNote(      MusicDeviceInstrumentID     inInstrument,
                                        MusicDeviceGroupID          inGroupID,
                                        NoteInstanceID *            outNoteInstanceID,
                                        UInt32                      inOffsetSampleFrame,
                                        const MusicDeviceNoteParams &inParams) { return kAudio_UnimplementedError; }

    /*! @method StopNote */
    virtual OSStatus    StopNote(       MusicDeviceGroupID          inGroupID,
                                        NoteInstanceID              inNoteInstanceID,
                                        UInt32                      inOffsetSampleFrame) { return kAudio_UnimplementedError; }
#endif

    // ________________________________________________________________________
    // ________________________________________________________________________
    // ________________________________________________________________________

protected:
#pragma mark -
#pragma mark Implementation methods

    /*! @method ReallocateBuffers */
    virtual void                ReallocateBuffers();
                                    // needs to be called when mMaxFramesPerSlice changes
    virtual void                DeallocateIOBuffers();

    /*! @method FillInParameterName */
    static void                 FillInParameterName (AudioUnitParameterInfo& ioInfo, CFStringRef inName, bool inShouldRelease)
    {
        ioInfo.cfNameString = inName;
        ioInfo.flags |= kAudioUnitParameterFlag_HasCFNameString;
        if (inShouldRelease)
            ioInfo.flags |= kAudioUnitParameterFlag_CFNameRelease;
        CFStringGetCString (inName, ioInfo.name, offsetof (AudioUnitParameterInfo, clumpID), kCFStringEncodingUTF8);
    }

    static void                 HasClump (AudioUnitParameterInfo& ioInfo, UInt32 inClumpID)
    {
        ioInfo.clumpID = inClumpID;
        ioInfo.flags |= kAudioUnitParameterFlag_HasClump;
    }

    /*! @method SetMaxFramesPerSlice */
    virtual void                SetMaxFramesPerSlice(UInt32 nFrames);

    /*! @method CanSetMaxFrames */
    virtual OSStatus            CanSetMaxFrames() const;

    /*! @method WantsRenderThreadID */
    bool                        WantsRenderThreadID () const { return mWantsRenderThreadID; }

    /*! @method SetWantsRenderThreadID */
    void                        SetWantsRenderThreadID (bool inFlag);

    /*! @method SetRenderError */
    OSStatus                    SetRenderError (OSStatus inErr)
    {
        if (inErr && mLastRenderError == 0) {
            mLastRenderError = inErr;
            PropertyChanged(kAudioUnitProperty_LastRenderError, kAudioUnitScope_Global, 0);
        }
        return inErr;
    }

private:
    /*! @method DoRenderBus */
    // shared between Render and RenderSlice, inlined to minimize function call overhead
    OSStatus                    DoRenderBus(            AudioUnitRenderActionFlags &    ioActionFlags,
                                                        const AudioTimeStamp &          inTimeStamp,
                                                        UInt32                          inBusNumber,
                                                        AUOutputElement *               theOutput,
                                                        UInt32                          inNumberFrames,
                                                        AudioBufferList &               ioData)
    {
        if (ioData.mBuffers[0].mData == NULL || (theOutput->WillAllocateBuffer() && Outputs().GetNumberOfElements() > 1))
            // will render into cache buffer
            theOutput->PrepareBuffer(inNumberFrames);
        else
            // will render into caller's buffer
            theOutput->SetBufferList(ioData);
        OSStatus result = RenderBus(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames);
        if (result == noErr) {
            if (ioData.mBuffers[0].mData == NULL) {
                theOutput->CopyBufferListTo(ioData);
                AUTRACE(kCATrace_AUBaseDoRenderBus, mComponentInstance, inNumberFrames, (intptr_t)theOutput->GetBufferList().mBuffers[0].mData, 0, *(UInt32 *)ioData.mBuffers[0].mData);
            } else {
                theOutput->CopyBufferContentsTo(ioData);
                AUTRACE(kCATrace_AUBaseDoRenderBus, mComponentInstance, inNumberFrames, (intptr_t)theOutput->GetBufferList().mBuffers[0].mData, (intptr_t)ioData.mBuffers[0].mData, *(UInt32 *)ioData.mBuffers[0].mData);
                theOutput->InvalidateBufferList();
            }
        }
        return result;
    }

    /*! @method HasIcon */
    bool                        HasIcon ();

    /*! @method ResetRenderTime */
    void                        ResetRenderTime ()
                                {
                                    memset (&mCurrentRenderTime, 0, sizeof(mCurrentRenderTime));
                                    mCurrentRenderTime.mSampleTime = kNoLastRenderedSampleTime;
                                }

protected:
    /*! @method GetAudioChannelLayout */
    virtual UInt32              GetChannelLayoutTags(   AudioUnitScope              scope,
                                                        AudioUnitElement            element,
                                                        AudioChannelLayoutTag *     outLayoutTags);

    /*! @method GetAudioChannelLayout */
    virtual UInt32              GetAudioChannelLayout(  AudioUnitScope              scope,
                                                        AudioUnitElement            element,
                                                        AudioChannelLayout *        outLayoutPtr,
                                                        Boolean &                   outWritable);

    /*! @method SetAudioChannelLayout */
    virtual OSStatus            SetAudioChannelLayout(  AudioUnitScope              scope,
                                                        AudioUnitElement            element,
                                                        const AudioChannelLayout *  inLayout);

    /*! @method RemoveAudioChannelLayout */
    virtual OSStatus            RemoveAudioChannelLayout(AudioUnitScope scope, AudioUnitElement element);

    /*! @method NeedsToRender */
    bool                        NeedsToRender(          const AudioTimeStamp &      inTimeStamp)
                                {
                                    bool needsToRender = fnotequal(inTimeStamp.mSampleTime, mCurrentRenderTime.mSampleTime);
                                    if (needsToRender)  // only copy this if we need to render
                                        mCurrentRenderTime = inTimeStamp;
                                    return needsToRender;
                                }

    // Scheduled parameter implementation:

    typedef std::vector<AudioUnitParameterEvent> ParameterEventList;

    // Usually, you won't override this method.  You only need to call this if your DSP code
    // is prepared to handle scheduled immediate and ramped parameter changes.
    // Before calling this method, it is assumed you have already called PullInput() on the input busses
    // for which the DSP code depends.  ProcessForScheduledParams() will call (potentially repeatedly)
    // virtual method ProcessScheduledSlice() to perform the actual DSP for a given sub-division of
    // the buffer.  The job of ProcessForScheduledParams() is to sub-divide the buffer into smaller
    // pieces according to the scheduled times found in the ParameterEventList (usually coming
    // directly from a previous call to ScheduleParameter() ), setting the appropriate immediate or
    // ramped parameter values for the corresponding scopes and elements, then calling ProcessScheduledSlice()
    // to do the actual DSP for each of these divisions.
    virtual OSStatus    ProcessForScheduledParams(  ParameterEventList      &inParamList,
                                                            UInt32                  inFramesToProcess,
                                                            void                    *inUserData );

    //  This method is called (potentially repeatedly) by ProcessForScheduledParams()
    //  in order to perform the actual DSP required for this portion of the entire buffer
    //  being processed.  The entire buffer can be divided up into smaller "slices"
    //  according to the timestamps on the scheduled parameters...
    //
    //  sub-classes wishing to handle scheduled parameter changes should override this method
    //  in order to do the appropriate DSP.  AUEffectBase already overrides this for standard
    //  effect AudioUnits.
    virtual OSStatus            ProcessScheduledSlice(  void                *inUserData,
                                                        UInt32              inStartFrameInBuffer,
                                                        UInt32              inSliceFramesToProcess,
                                                        UInt32              inTotalBufferFrames ) {return noErr;};  // default impl does nothing...


    /*! @method CurrentRenderTime */
    const AudioTimeStamp &      CurrentRenderTime () const { return mCurrentRenderTime; }

    // ________________________________________________________________________
    //  Private data members to discourage hacking in subclasses
private:
    struct RenderCallback {
        RenderCallback(AURenderCallback proc, void *ref) :
            mRenderNotify(proc),
            mRenderNotifyRefCon(ref)
        { }

        AURenderCallback            mRenderNotify;
        void *                      mRenderNotifyRefCon;

        bool operator == (const RenderCallback &other) {
            return this->mRenderNotify == other.mRenderNotify &&
                    this->mRenderNotifyRefCon == other.mRenderNotifyRefCon;
        }
    };
    typedef TThreadSafeList<RenderCallback> RenderCallbackList;

protected:
#if !CA_BASIC_AU_FEATURES
    enum { kNumScopes = 4 };
#else
    enum { kNumScopes = 3 };
#endif

private:
    /*! @var mElementsCreated */
    bool                        mElementsCreated;
protected:
    /*! @var mInitialized */
    bool                        mInitialized;
    /*! @var mHasBegunInitializing */
    bool                        mHasBegunInitializing;
private:
    /*! @var mAudioUnitAPIVersion */
    UInt8                       mAudioUnitAPIVersion;

    /*! @var mInitNumInputEls */
    const UInt32                mInitNumInputEls;
    /*! @var mInitNumOutputEls */
    const UInt32                mInitNumOutputEls;
#if !CA_BASIC_AU_FEATURES
    /*! @var mInitNumGroupEls */
    const UInt32                mInitNumGroupEls;
#endif
    /*! @var mScopes */
    AUScope                     mScopes[kNumScopes];

    /*! @var mRenderCallbacks */
    RenderCallbackList          mRenderCallbacks;
    bool                        mRenderCallbacksTouched;

    /*! @var mRenderThreadID */
#if TARGET_OS_MAC
    pthread_t                   mRenderThreadID;
#elif TARGET_OS_WIN32
    UInt32                      mRenderThreadID;
#endif

    /*! @var mWantsRenderThreadID */
    bool                        mWantsRenderThreadID;

    /*! @var mCurrentRenderTime */
    AudioTimeStamp              mCurrentRenderTime;

    /*! @var mMaxFramesPerSlice */
    UInt32                      mMaxFramesPerSlice;

    /*! @var mLastRenderError */
    OSStatus                    mLastRenderError;
    /*! @var mCurrentPreset */
    AUPreset                    mCurrentPreset;

protected:
    /*! @var mUsesFixedBlockSize */
    bool                        mUsesFixedBlockSize;

    struct PropertyListener {
        AudioUnitPropertyID             propertyID;
        AudioUnitPropertyListenerProc   listenerProc;
        void *                          listenerRefCon;
    };
    typedef std::vector<PropertyListener>   PropertyListeners;

    /*! @var mParamList */
    ParameterEventList          mParamList;
    /*! @var mPropertyListeners */
    PropertyListeners           mPropertyListeners;

    /*! @var mBuffersAllocated */
    bool                        mBuffersAllocated;

    /*! @var mLogString */
    // if this is NOT null, it will contain identifying info about this AU.
    char*                       mLogString;

    /*! @var mNickName */
    CFStringRef                 mNickName;

    /*! @var mAUMutex */
    CAMutex *                   mAUMutex;

private:
    /*! @var sVectorUnitType */
    static SInt32   sVectorUnitType;

#if !CA_NO_AU_HOST_CALLBACKS
protected:
    /*! @var mHostCallbackInfo */
    HostCallbackInfo            mHostCallbackInfo;

#endif
#if !CA_NO_AU_UI_FEATURES
protected:
    /*! @var mContextInfo */
    CFStringRef                 mContextName;
#endif
};

inline  OSStatus    AUInputElement::PullInputWithBufferList(
                                                AudioUnitRenderActionFlags &    ioActionFlags,
                                                const AudioTimeStamp &          inTimeStamp,
                                                AudioUnitElement                inElement,
                                                UInt32                          nFrames,
                                                AudioBufferList *               inBufferList)
{
    OSStatus theResult;

    if (HasConnection()) {
            // only support connections for V2 audio units
#if !CA_USE_AUDIO_PLUGIN_ONLY
        if (mConnRenderProc != NULL)
            theResult = reinterpret_cast<AudioUnitRenderProc>(mConnRenderProc)(
                            mConnInstanceStorage, &ioActionFlags, &inTimeStamp, mConnection.sourceOutputNumber, nFrames, inBufferList);
        else
#endif
            theResult = AudioUnitRender(
                            mConnection.sourceAudioUnit, &ioActionFlags, &inTimeStamp, mConnection.sourceOutputNumber, nFrames, inBufferList);
    } else {
        // kFromCallback:
            theResult = (mInputProc)(
                            mInputProcRefCon, &ioActionFlags, &inTimeStamp, inElement, nFrames, inBufferList);
    }

    if (mInputType == kNoInput) // defense: the guy upstream could have disconnected
                                // it's a horrible thing to do, but may happen!
        return kAudioUnitErr_NoConnection;


    return theResult;
}

#endif // __AUBase_h__
